// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

#include <tuple>
#include <functional>
#include <unordered_map>
#include <type_traits>
#include <folly/dynamic.h>

#ifdef _M_IX86

#define CALL_MEMBER(FUNC) \
    FUNC(__cdecl) \
    FUNC(__stdcall) \
    FUNC(__thiscall) \
    FUNC(__vectorcall)
#define CALL_NON_MEMBER(FUNC) \
    FUNC(__cdecl) \
    FUNC(__stdcall) \
    FUNC(__vectorcall)
#else
#define CALL_MEMBER(FUNC) \
    FUNC(__cdecl)
#define CALL_NON_MEMBER(FUNC) \
    FUNC(__cdecl)
#endif

template<size_t TIndex, typename TFunc>
struct get_argument_type_impl;

#define GET_ARGUMENT_TYPE_IMPL(CALL_OPT) \
template<size_t TIndex, typename TRet, typename ...TArgs> \
struct get_argument_type_impl<TIndex, TRet CALL_OPT (TArgs...)> \
{ \
  typedef typename std::tuple_element<TIndex, std::tuple<TArgs...>>::type type; \
};

CALL_NON_MEMBER(GET_ARGUMENT_TYPE_IMPL)
#undef GET_ARGUMENT_TYPE_IMPL

#define GET_ARGUMENT_TYPE_IMPL(CALL_OPT) \
template<size_t TIndex, typename TType, typename TRet, typename ...TArgs> \
struct get_argument_type_impl<TIndex, TRet(CALL_OPT TType::*)(TArgs...)> \
{ \
  typedef typename std::tuple_element<TIndex, std::tuple<TArgs...>>::type type; \
};

CALL_MEMBER(GET_ARGUMENT_TYPE_IMPL)
#undef GET_ARGUMENT_TYPE_IMPL

template<size_t TIndex, typename TFunc>
struct get_argument_type
{
  typedef typename get_argument_type_impl<TIndex, TFunc>::type type;
};

template<typename T>
struct json_type_traits;
//{
//  static_assert(false, "implement type traits to support parsing of your type form folly::dynamic");
//};

template<typename T>
struct json_type_traits<std::vector<T>>
{
  static std::vector<T> parseJson(folly::dynamic& obj)
  {
    std::vector<T> result;
    for (auto& item : obj)
    {
      result.push_back(json_type_traits<T>::parseJson(item));
    }
    return result;
  }
};

template<>
struct json_type_traits<folly::dynamic>
{
  static folly::dynamic& parseJson(folly::dynamic& obj)
  {
    return obj;
  }
};

template<>
struct json_type_traits<double>
{
  static double parseJson(folly::dynamic& obj)
  {
    return obj.asDouble();
  }
};

template<>
struct json_type_traits<std::string>
{
  static std::string parseJson(folly::dynamic& obj)
  {
    return obj.asString();
  }
};

#define RCT_BEGIN_PROPERTY_MAP(className) \
  std::unordered_map<std::string, std::function<void(react::uwp::XamlView&, folly::dynamic&)>> m_propertyHandlers;\
  void SetupPropertyHandlersInternal() { \
  typedef className class_name;

#define RCT_PROPERTY(name, handler) \
  { \
    typedef typename get_argument_type<1, decltype(&class_name::handler)>::type second_argument_t; \
    typedef typename std::remove_const_t<std::remove_reference_t<second_argument_t>> argument_type_t; \
    m_propertyHandlers[name] = [this](react::uwp::XamlView& view, folly::dynamic& prop){ \
      typedef decltype(json_type_traits<argument_type_t>::parseJson(prop)) value_type_t; \
      value_type_t value = json_type_traits<argument_type_t>::parseJson(prop); \
      handler(view, value); \
    }; \
  }


#define RCT_END_PROPERTY_MAP() } \
  void UpdatePropertiesInternal(react::uwp::XamlView& view, folly::dynamic& diffMap) { \
    for (auto& pair : diffMap.items()) \
    { \
      auto propHandler = m_propertyHandlers.find(pair.first.asString()); \
      if (propHandler != m_propertyHandlers.end()) \
      { \
        propHandler->second(view, pair.second); \
      } \
    } \
  }
